package net.leieuncretino.beta.parole.ui;

import java.util.ListIterator;

import net.leieuncretino.beta.parole.R;
import net.leieuncretino.beta.parole.impl.Partita;
import net.leieuncretino.beta.parole.util.SyncRunnable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.view.MotionEvent;
import android.view.View;

public class ParoleView extends View implements SyncRunnable.Event, Partita.RotateHandler {
	protected static final String TAG = "ParoleView";
	public static final int PADDING = 10;
	public static final int REDRAW_FREQ = 10;

	private FingerTracker mFingerTracker;

	private Partita partita;
	private int timeRemaining;
	private int redrawCount;

	private int width;
	private int height;
	private int gridsize;
	private float boxsize = 0.0f;

	private int boardWidth;
	private String currentWord;

	private Paint p;
	private long highlighted;
	
	private Bitmap bgBox;

	public ParoleView(Context context, Partita g) {
		super(context);

		partita = g;
		boardWidth = partita.getBoard().getWidth();

		mFingerTracker = new FingerTracker(this,partita);

		timeRemaining = 0;
		redrawCount = 1;

		p = new Paint();
		p.setTextAlign(Paint.Align.CENTER);
		p.setAntiAlias(true);
		p.setStrokeWidth(2);

		
		setFocusable(true);

		g.setRotateHandler(this);
	}

	private void setDimensions(int w, int h) {
		// Log.d(TAG,"setDimensions:"+w+","+h);

		width = w;
		height = h;

		gridsize = Math.min(width,height) - 2*PADDING;
		float newboxsize = ((float) gridsize) / boardWidth;

		if (newboxsize != boxsize) {
			/** Load and resize the bitmap */
			Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(), R.drawable.bg_box1);
			
	        int width = bitmapOrg.getWidth();
	        int height = bitmapOrg.getHeight();
	        float newWidth = newboxsize;
	        float newHeight = newboxsize;
	       
	        // calculate the scale - in this case = 0.4f
	        float scaleWidth = (newWidth) / width;
	        float scaleHeight = (newHeight) / height;
	       
	        // createa matrix for the manipulation
	        Matrix matrix = new Matrix();
	        // resize the bit map
	        matrix.postScale(scaleWidth, scaleHeight);
	        // rotate the Bitmap
	        // matrix.postRotate(45);
	 
	        // recreate the new Bitmap
	        bgBox = Bitmap.createBitmap(bitmapOrg, 0, 0,
	                          width, height, matrix, true);
			
		}
		
		
		boxsize = newboxsize;

		if(mFingerTracker != null) {
			mFingerTracker.boundBoard(PADDING,PADDING,
				PADDING+gridsize,PADDING+gridsize);
		}
	}

	private void drawBoard(Canvas canvas) {
		// Draw white box
		p.setARGB(255,255,255,255);
		canvas.drawRect(PADDING,PADDING,gridsize+PADDING,gridsize+PADDING,p);
		
		// Draw Boxes
		for(int i=0;i<partita.getBoard().getSize();i++) {
			// if(((1<<i)&highlighted) == 0) continue;
			int x = i % partita.getBoard().getWidth();
			int y = i / partita.getBoard().getWidth();
			float left = PADDING + boxsize * x;
			float top = PADDING + boxsize * y;
//			float right = PADDING + boxsize * (x+1);
//			float bottom = PADDING + boxsize * (y+1);
			canvas.drawBitmap(bgBox, left, top, p);
		}
		
		// Draw touched boxes
		p.setARGB(196,255,255,0);
		for(int i=0;i<partita.getBoard().getSize();i++) {
			if(((1L<<i)&highlighted) == 0) continue;
			int x = i % partita.getBoard().getWidth();
			int y = i / partita.getBoard().getWidth();
			float left = PADDING + boxsize * x;
			float top = PADDING + boxsize * y;
			float right = PADDING + boxsize * (x+1);
			float bottom = PADDING + boxsize * (y+1);
			// canvas.drawRect(left,top,right,bottom,p);
			float cx = left + ((right - left) / 2);
			float cy = top + ((bottom - top) / 2 );
			float radius = boxsize /2;
			canvas.drawCircle(cx, cy, radius, p);
		}

		
		// Draw Word Trail
		p.setARGB(255,255,128,0);
		float oldStrokeW = p.getStrokeWidth();
		p.setStrokeWidth(8.0f);
		int lastPoint = -1;
		float lastCx = 0f;
		float lastCy = 0f;
		for (int point = 0; point < mFingerTracker.numTouched; point++) {
			int i = mFingerTracker.touched[point];
			int x = i % partita.getBoard().getWidth();
			int y = i / partita.getBoard().getWidth();
			float left = PADDING + boxsize * x;
			float top = PADDING + boxsize * y;
			float right = PADDING + boxsize * (x+1);
			float bottom = PADDING + boxsize * (y+1);
			// canvas.drawRect(left,top,right,bottom,p);
			float cx = left + ((right - left) / 2);
			float cy = top + ((bottom - top) / 2 );
			if (lastPoint > -1) {
				canvas.drawLine(cx, cy, lastCx, lastCy, p);
			}
			lastPoint = i;
			lastCx = cx;
			lastCy = cy;
		}

		p.setStrokeWidth(oldStrokeW);

// TODO Work on text size and position for small screen !!1		
		
		p.setARGB(255,0,0,0);
		p.setTextSize(boxsize-20);
		p.setTextAlign(Paint.Align.CENTER);

		p.setTypeface(Typeface.MONOSPACE);
		for(int x=0;x<boardWidth;x++) {
			for(int y=0;y<boardWidth;y++) {
				int i = x + (y * boardWidth);
				if(((1L<<i)&highlighted) == 0) {
					p.setARGB(255,255,255,255);
				} else {
					p.setARGB(255,0,0,0);
				}
				String txt = partita.getBoard().elementAt(x,y);
				canvas.drawText(txt,PADDING+x*boxsize+boxsize/2,
						PADDING-15+(y+1)*boxsize,p);
//				canvas.drawText(txt,PADDING+x*boxsize+boxsize/2,
//						PADDING-10+(y+1)*boxsize,p);
			}
		}

	}

	private void drawTimer(Canvas canvas) {
		if(timeRemaining < 1000) {
			p.setARGB(255,255,0,0);
		} else if (timeRemaining < 3000) {
			p.setARGB(255,255,255,0);
		} else {
			p.setARGB(255,0,255,0);
		}
		for(int i=0;i<5;i++) {
			canvas.drawLine(0,i,width*timeRemaining/partita.getMaxTimeRemaining(),
				i,p);
		}
	}

	private void drawWordCount(Canvas canvas, int left, int top) {
		p.setTypeface(Typeface.SANS_SERIF);
		p.setARGB(255,0,0,0);
		
		p.setTextSize(30);
		canvas.drawText(""+partita.getWordCount()+"/"+partita.getMaxWordCount(),
			left,top,p);

		p.setTextSize(20);
		canvas.drawText("WORDS",left,top+20,p);
	}

	private void drawWordList(Canvas canvas, int left, int top, int bottom) {
		// draw current word
		p.setTextSize(20);
		p.setARGB(255,0,0,0);
		if(currentWord != null) {
			canvas.drawText(currentWord,left,top,p);
		}

		// draw words
		int pos = top+20;
		ListIterator<String> li = partita.listIterator();
		p.setTextSize(16);

		while(li.hasNext() && pos < bottom) {
			String w = li.next();
			if(partita.isWord(w)) {
				p.setARGB(255,0,0,0);
			} else {
				p.setARGB(255,255,0,0);
			}
			canvas.drawText(w,left,pos,p);
			pos += 16;
		}
	}

	private void drawTextTimer(Canvas canvas, int left, int top) {
		if(timeRemaining < 1000) {
			p.setARGB(255,255,0,0);
		} else if (timeRemaining < 3000) {
			p.setARGB(255,255,255,0);
		} else {
			p.setARGB(255,0,0,0);
		}

		int secRemaining = timeRemaining / 100;
		int mins = secRemaining / 60;
		int secs = secRemaining % 60;

		String time = "" + mins + ":";
		if(secs < 10) {
			time += "0"+ (secRemaining % 60);
		} else {
			time += ""+ (secRemaining % 60);
		}

		p.setTextSize(30);
		canvas.drawText(time,left,top,p);
	}

	private void drawScoreLandscape(Canvas canvas) {
		int textareatop = PADDING;
		int textareaheight = height - 2*PADDING;
		int textarealeft = 2*PADDING + gridsize;
		int textareawidth = width - PADDING - textarealeft;
	
		drawWordCount(canvas,textarealeft + textareawidth/2,
			textareatop+30);
		
		drawWordList(canvas, textarealeft + textareawidth/2, textareatop+110,
			textareatop+textareaheight);

		drawTextTimer(canvas,textarealeft + textareawidth/2,textareatop+80);
	}

	private void drawScorePortrait(Canvas canvas) {
		int textareatop = 2*PADDING + gridsize;
		int textareaheight = height - PADDING - textareatop;
		int textarealeft = PADDING;
		int textareawidth = width - 2*PADDING;

		p.setTypeface(Typeface.SANS_SERIF);

		drawWordCount(canvas,textarealeft + textareawidth/4,
			textareatop+30);
		
		drawWordList(canvas, textarealeft + textareawidth*3/4, textareatop+20,
			textareatop+textareaheight);

		drawTextTimer(canvas,textarealeft + textareawidth/4,textareatop+80);

	}

	@Override
	public void onDraw(Canvas canvas) {
		// Log.d(TAG,"onDraw starts, canvas="+canvas);
		setDimensions(getMeasuredWidth(),getMeasuredHeight());
		// Log.d(TAG, "onDraw:"+width+","+height);

		// canvas.drawRGB(0x99,0xcc,0xff);
		
		canvas.drawColor(getResources().getColor(R.color.bg_default));

		if(partita.getStatus() != Partita.GameStatus.RUNNING) return;

		drawBoard(canvas);
		drawTimer(canvas);

		if(width > height) {
			drawScoreLandscape(canvas);
		} else {
			drawScorePortrait(canvas);
		}
		// Log.d(TAG,"onDraw stops");
	}

	@Override 
	public boolean onTouchEvent(MotionEvent event) {
		// Log.d(TAG,"onTouchEvent() starts");
		if(mFingerTracker == null) return false;
		int action = event.getAction();
		switch(action) {
			case MotionEvent.ACTION_DOWN:
			case MotionEvent.ACTION_MOVE:
				mFingerTracker.touchScreen((int)event.getX(),(int)event.getY());
			break;
			case MotionEvent.ACTION_UP:
				mFingerTracker.release();
			break;
		}

		redraw();
		// Log.d(TAG,"onTouchEvent() ends");
		return true;
	}


	public void redraw() {
		redrawCount = REDRAW_FREQ;
		invalidate();
	}

	public void tick(int time) {
		boolean doRedraw = false;
	
		timeRemaining = time;
		if(--redrawCount <= 0) {
			doRedraw = true;
		}
		if(doRedraw) {
			redraw();
		}
	}

	public void onRotate() {
		mFingerTracker.reset();
	}

	private class FingerTracker {
		private ParoleView view;
		private Partita game;
		
		private int numTouched;
		private byte touched[];
		private long touchedBits;

		private byte touching;

		private int left;
		private int top;
		private int width;
		private int height;

		private int box_width;
		private int radius_squared;

		FingerTracker(ParoleView v, Partita g) {
			view = v;
			game = g;
			touched = new byte[game.getBoard().getSize()];
			touchedBits = 0;

			reset();
		}

		private void reset() {
			// Log.d(TAG,"RESET");
			for(int i=0;i<touched.length;i++) {
				touched[i] = -1;
			}

			if(numTouched > 0) {
				highlighted = 0;
			}
			touchedBits = 0;
			numTouched = 0;
			touching = -1;
		}
		
		private void countTouch() {
			long touchBit = 1L << touching;
			if((touchedBits & touchBit) > 0) return;

			touched[numTouched] = touching;
			touchedBits |= 1L << touching;
			highlighted = touchedBits;
			numTouched++;
			redraw();
		}

		void touchScreen(int x, int y) {
			if(x < left || x >= (left+width)) return;
			if(y < top || y >= (top+height)) return;


			int bx = (x-left)*boardWidth/width;
			int by = (y-top)*boardWidth/height;

			touchBox(bx,by);
			
			if(canTouch(bx+boardWidth*by) && nearCenter(x,y,bx,by)) {
				countTouch();
			}
		}

		private boolean canTouch(int box) {
			long boxBits = 1L<<box;
			currentWord = getWord();
			if((boxBits & touchedBits) > 0) return false;

			return (boxBits & game.getBoard().transitions(touched[numTouched-1]))>0;
		}

		private void touchBox(int x, int y) {
			int box = x+boardWidth*y;
			
			if(touching < 0) {
				touching = (byte) box;
				countTouch();
			} else if(touching != box && canTouch(box)) {
				touching = (byte) box;
			}

		}

		private boolean nearCenter(int x, int y, int bx, int by) {
			int cx,cy;

			cx = left + bx * box_width + box_width / 2;
			cy = top + by * box_width + box_width / 2;

			int d_squared = (cx-x)*(cx-x)+(cy-y)*(cy-y);

			return d_squared < radius_squared;
		}

		void boundBoard(int l, int t, int w, int h) {
			left = l;
			top = t;
			width = w;
			height = h;

			box_width = width / boardWidth;

			radius_squared = box_width / 3;
			radius_squared *= radius_squared;
		}

		String getWord() {
			String ret = "";

			for(int i=0;i<numTouched;i++) {
				ret += game.getBoard().elementAt(touched[i]).toUpperCase();
			}
	
			return ret;
		}

		void release() {
			if(numTouched > 0) {
				String s = getWord();

				game.addWord(s);
				currentWord = null;
			}


			reset();
		}
	}
}
